#pragma once

#include "detail/checked_delete.h"
#include <cassert>
#include <cstddef>


namespace evm {

template <typename T>
class scoped_array {
private:
    T* px;

    scoped_array(const scoped_array&) = delete;
    scoped_array& operator=(const scoped_array&) = delete;

    void operator==(const scoped_array&) const = delete;
    void operator!=(const scoped_array&) const = delete;

public:
    typedef T element_type;

    explicit scoped_array(T* p = nullptr) noexcept : px(p) {}

    ~scoped_array() {
        checked_array_delete(px);
    }

    void reset(T* p = nullptr) {
        assert(p == nullptr || p != px);
        scoped_array<T>(p).swap(*this);
    }

    T& operator[](std::ptrdiff_t i) const {
        assert(px != 0);
        assert(i >= 0);
        return px[i];
    }

    T* get() const noexcept {
        return px;
    }

    operator bool() const noexcept {
        return px != nullptr;
    }

    void swap(scoped_array& other) noexcept {
        T* tmp = other.px;
        other.px = px;
        px = tmp;
    }
};

template <typename T>
inline bool operator==(const scoped_array<T>& p, std::nullptr_t) noexcept {
    return p.get() == nullptr;
}

template <typename T>
inline bool operator==(std::nullptr_t, const scoped_array<T>& p) noexcept {
    return p.get() == nullptr;
}

template <typename T>
inline bool operator!=(const scoped_array<T>& p, std::nullptr_t) noexcept {
    return p.get() != nullptr;
}

template <typename T>
inline bool operator!=(std::nullptr_t, const scoped_array<T>& p) noexcept {
    return p.get() != nullptr;
}

template <typename T>
inline void swap(scoped_array<T>& a, scoped_array<T>& b) noexcept {
    a.swap(b);
}

}
